package org.cqust.backend.service.mission;

import lombok.RequiredArgsConstructor;
import org.cqust.backend.dto.response.mission.attempt.MissionStepAttemptDTO;
import org.cqust.backend.entity.mission.attempt.MissionStepAttempt;
import org.cqust.backend.exception.BusinessException;
import org.cqust.backend.dto.request.mission.MissionCreateRequestDTO;
import org.cqust.backend.dto.request.mission.MissionUpdateRequestDTO;
import org.cqust.backend.dto.response.mission.attempt.MissionAttemptDTO;
import org.cqust.backend.dto.response.mission.MissionCreateDTO;
import org.cqust.backend.dto.response.mission.MissionDTO;
import org.cqust.backend.dto.response.mission.MissionVersionDetailDTO;
import org.cqust.backend.entity.mission.Mission;
import org.cqust.backend.entity.mission.MissionVersion;
import org.cqust.backend.entity.mission.attempt.MissionAttempt;
import org.cqust.backend.repository.mission.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
public class MissionService {
    private final MissionRepository missionRepository;
    private final MissionVersionRepository missionVersionRepository;
    private final MissionStepVersionRepository missionStepVersionRepository;
    private final MissionAttemptRepository missionAttemptRepository;
    private final MissionStepAttemptRepository missionStepAttemptRepository;



    // 检索所有可用的mission
    public List<MissionDTO> getAvailableMissions() {
        List<Mission> missionList = missionRepository.findByPublishedTrue();
        return missionList.stream().map(MissionDTO::new).toList();
    }

    // 检索mission
    public MissionDTO getMissionById(Long missionId) {
        Mission mission = missionRepository.findById(missionId)
                .orElseThrow(() -> new BusinessException("未找到ID为 " + missionId + " 的任务"));
        return new MissionDTO(mission);
    }

    // 带版本控制的更新
    // MissionDTO需要包含：missionId
    @Transactional
    public void updateMissionWithVersionControl(MissionUpdateRequestDTO missionUpdateRequestDTO) {
        Mission mission = missionRepository.findById(missionUpdateRequestDTO.getMissionId())
                .orElseThrow(() -> new BusinessException("未找到ID为 " + missionUpdateRequestDTO.getMissionId() + " 的任务"));
        MissionVersion currentVersion = missionVersionRepository.findById(mission.getMissionVersion().getId())
                .orElseThrow(() -> new BusinessException("ID为 " + missionUpdateRequestDTO.getMissionId() + " 所指代版本不存在"));
        if (currentVersion.getObsolete()) {
            throw new BusinessException("不允许修改已弃用的数据");
        }

        //是否有学生已经做了这个任务？
        boolean hasAttempts = missionAttemptRepository.existsByMissionVersionId(mission.getMissionVersion().getId());

        //做过了任务，则创建新的version，更新Mission的versionId
        if (hasAttempts) {
            archiveOldMissionVersions(mission);
            createMissionVersion(missionUpdateRequestDTO, mission);
        }
        else {
            updateCurrentVersion(currentVersion, missionUpdateRequestDTO);
        }
    }

    private void updateCurrentVersion(MissionVersion currentVersion, MissionUpdateRequestDTO dto) {
        currentVersion.setTitle(dto.getTitle());
        currentVersion.setDescription(dto.getDescription());
        currentVersion.setMaxScore(dto.getMaxScore());
        currentVersion.setDuration(dto.getDuration());
        currentVersion.setInstructions(dto.getInstructions());
        missionVersionRepository.save(currentVersion);
    }

    //归档旧版本
    private void archiveOldMissionVersions(Mission mission){
        //同一个missionId，只能同时存在一个Obsolete为false的项目
        missionVersionRepository.updateAllByMissionVersionIdAndSetObsoleteTrue(mission.getId());
    }

    @Transactional
    public MissionCreateDTO createNewMission(MissionCreateRequestDTO createRequestDTO) {
        Mission mission = new Mission();
        mission.setId(null);
        mission.setPublished(createRequestDTO.getPublished());
        mission = missionRepository.save(mission);
        //保存版本
        MissionVersion missionVersion = createMissionVersion(createRequestDTO, mission);

        MissionCreateDTO responseDTO = new MissionCreateDTO();
        responseDTO.setMissionId(mission.getId());
        responseDTO.setMissionVersionId(missionVersion.getId());
        return responseDTO;
    }

    private MissionVersion createMissionVersion(MissionVersionDetailDTO versionDTO, Mission mission) {
        MissionVersion missionVersion = new MissionVersion();
        missionVersion.setId(null);
        missionVersion.setDuration(versionDTO.getDuration());
        missionVersion.setMaxScore(versionDTO.getMaxScore());
        missionVersion.setInstructions(versionDTO.getInstructions());
        missionVersion.setMission(mission);
        missionVersion.setTitle(versionDTO.getTitle());
        missionVersion.setDescription(versionDTO.getDescription());
        missionVersion.setObsolete(false);
        missionVersion = missionVersionRepository.save(missionVersion);
        //更新mission所对应的版本
        missionRepository.updateByMissionIdAndSetMissionVersionId(mission.getId(),missionVersion.getId());
        return missionVersion;
    }

    public List<MissionAttemptDTO> getMissionAttemptsByUserId(Long userId) {
        List<MissionAttempt> missionAttemptList = missionAttemptRepository.findByUserId(userId);
        return missionAttemptList.stream()
                .map(MissionAttemptDTO::new)
                .toList();
    }
    public List<MissionAttemptDTO> getAllMissionAttempts(){
        List<MissionAttempt> missionAttemptList = missionAttemptRepository.findAll();
        return missionAttemptList.stream()
                .map(MissionAttemptDTO::new)
                .toList();
    }
    public List<MissionStepAttemptDTO> getAllMissionStepAttempts(Long missionAttemptId){
        List<MissionStepAttempt> missionStepAttempts = missionStepAttemptRepository.findAllByMissionAttemptId(missionAttemptId);

        return missionStepAttempts.stream()
                .map((o) ->new MissionStepAttemptDTO(o,missionStepVersionRepository.findById(o.getStepVersionId()).orElse(null)))
                .toList();
    }
}

